\section{C++ 相关名称}
\subsection{直接初始化}\label{direct_initialization}
从明确的构造函数实参的集合初始化对象。
\begin{cpp}
T object ( arg );
T object ( arg1, arg2, ... );(1)    
T object { arg };    (2)    (since C++11)
T ( other )
T ( arg1, arg2, ... )(3)    
static_cast< T >( other )    (4)    
new T(args, ...)    (5)    
Class::Class() : member(args, ...) { ... }    (6)    
[arg](){ ... }    (7)    (since C++11)
\end{cpp}
在下列场合进行直接初始化：
\begin{enumerate}
    \item 表达式或\textbf{花括号初始化器列表} (C++11 起)的非空带括号列表初始化。
    \item 以花括号环绕的单个初始化器初始化一个非类类型对象（注意：对于类类型和其他使用\textbf{花括号初始化器列表}的初始化，见列表初始化）
    \item 用函数式转型或以带括号的表达式列表初始化纯右值临时量 (C++17 前)纯右值的结果对象 (C++17 起)。
    \item 用 static\_cast 表达式初始化纯右值临时量 (C++17 前)纯右值的结果对象 (C++17 起)。
    \item 用带有非空初始化器的 new 表达式初始化具有动态存储期的对象。
    \item 用构造函数初始化器列表初始化基类或非静态成员。
    \item 在 lambda 表达式中从按复制捕获的变量初始化闭包对象的成员。
\end{enumerate}
直接初始化的效果是：
\begin{itemize}
\item 如果 T 是数组类型，那么
\begin{itemize}
\item 程序非良构。(C++20 前)
\item 按聚合初始化初始化数组，但允许窄化转换，并值初始化任何不带初始化器的元素。
\begin{cpp}
struct A
{
    explicit A(int i = 0) {}
};
A a[2](A(1)); // OK：以 A(1) 初始化 a[0] 并以 A() 初始化 a[1]
A b[2]{A(1)}; // 错误：从 {} 隐式复制初始化 b[1] 选择了 explicit 构造函数 //(C++20 起)
\end{cpp} 
\end{itemize}
\item 如果 T 是类类型，
\begin{itemize}
\item 如果初始化器是纯右值表达式且类型与 T 为相同的类（忽略 cv 限定），则用初始化器表达式自身，而非从它实质化的临时量，初始化目标对象：
（ C++17 前，编译器可以在此情况下消除源自纯右值的构造，但适合的构造函数仍必须可访问：参阅复制消除）(C++17 起)
\item 检验 T 的构造函数并由重载决议选取最佳匹配。然后调用该构造函数初始化对象。
\item 否则，如果目标类型是（可能有 cv 限定）的聚合类，则按聚合初始化中所述进行初始化，但允许窄化转换，不允许指派初始化器，不延长引用所绑定到的临时量的生存期，不进行花括号消除，并值初始化任何无初始化器的元素。
\begin{cpp}
struct B
{
    int a;
    int&& r;
};
 
int f();
int n = 10;
 
B b1{1, f()};            // OK：延长生存期
B b2(1, f());            // 良构，但有悬垂引用
B b3{1.0, 1};            // 错误：窄化转换
B b4(1.0, 1);            // 良构，但有悬垂引用
B b5(1.0, std::move(n)); // OK
(C++20 起)
\end{cpp}
\end{itemize}
\item 否则，如果 T 是非类类型但源类型是类类型，则检验源类型及其各基类的转换函数，并由重载决议选取最佳匹配。然后用选取的用户定义转换，转换初始化器表达式为所初始化的对象。
\item 否则，如果 T 是 bool 而原类型是 std::nullptr\_t，则被初始化对象的值为 false。
\item 否则，在必要时使用标准转换，转换 其他对象 的值为 T 的无 cv 限定版本，而所初始化的对象的初值为（可能为转换后的）该值。
\end{itemize}
\begin{remark}
直接初始比复制初始化更宽松：复制初始化仅考虑非 explicit 构造函数和非 explicit 的用户定义转换函数，而直接初始化考虑所有构造函数和所有用户定义转换函数。

在使用直接初始化语法 (1)（带圆括号）的变量声明和函数声明之间有歧义的情况下，编译器始终选择函数声明。此消歧义规则有时是反直觉的，并且已被称为最烦人的分析。
\end{remark}
\begin{cpp}
#include <iterator>
#include <string>
#include <fstream>
int main()
{
    std::ifstream file("data.txt");
    // 下面是函数声明：
    std::string str(std::istreambuf_iterator<char>(file),
                    std::istreambuf_iterator<char>());
    // 它声明名为 str 的函数，其返回类型为 std::string，
    // 第一参数拥有 std::istreambuf_iterator<char> 类型和名称 "file"
    // 第二参数无名称并拥有类型 std::istreambuf_iterator<char>()，
    // 它被重写成函数指针类型 std::istreambuf_iterator<char>(*)()
 
    // C++11 前的修正：用额外括号环绕其中一个实参
    std::string str( (std::istreambuf_iterator<char>(file) ),
                      std::istreambuf_iterator<char>());
 
    // C++11 后的修正：列表初始化任意实参
    std::string str(std::istreambuf_iterator<char>{file}, {});
}
\end{cpp}
运行此代码
\begin{cpp}
#include <string>
#include <iostream>
#include <memory>
 
struct Foo
{
    int mem;
    explicit Foo(int n) : mem(n) {}
};
 
int main()
{
    std::string s1("test"); // 自 const char* 的构造函数
    std::string s2(10, 'a');
 
    std::unique_ptr<int> p(new int(1)); // OK：允许 explicit 构造函数
//  std::unique_ptr<int> p = new int(1); // 错误：构造函数为 explicit
 
    Foo f(2); // f 被直接初始化：
              // 构造函数形参 n 从右值 2 复制初始化
              // f.mem 从形参 n 直接初始化
//  Foo f2 = 2; // 错误：构造函数为 explicit
 
    std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem  << '\n';
}
\end{cpp}
输出：
\begin{bash}
test aaaaaaaaaa 1 2
\end{bash}
\subsection{列表初始化}\label{list_initialize}
\begin{cpp}
# 直接列表初始化
T object { arg1, arg2, ... };    (1)    
T { arg1, arg2, ... }    (2)    
new T { arg1, arg2, ... }    (3)    
Class { T member { arg1, arg2, ... }; };    (4)    
Class::Class() : member { arg1, arg2, ... } {...    (5)    
# 拷贝列表初始化
T object = { arg1, arg2, ... };    (6)    
function ({ arg1, arg2, ... })    (7)    
return { arg1, arg2, ... };    (8)    
object [{ arg1, arg2, ... }]    (9)    
object = { arg1, arg2, ... }    (10)    
U ({ arg1, arg2, ... })    (11)    
Class { T member = { arg1, arg2, ... }; };    (12)    
\end{cpp}
在下列情形进行列表初始化：

直接列表初始化（考虑 explicit 和非 explicit 构造函数）
\begin{enumerate}
    \item 以 \textbf{花括号初始化器列表}（即花括号环绕的可以为空的表达式或\textbf{花括号初始化器列表}的列表）初始化具名变量
    \item 以\textbf{花括号初始化器列表}初始化无名临时量
    \item 以 new 表达式初始化具有动态存储期的对象，它的初始化器是\textbf{花括号初始化器列表}列表
    \item 在不使用等号的非静态数据成员初始化器中
    \item 在构造函数的成员初始化列表中，如果使用 \textbf{花括号初始化器列表}
    \item 列表初始化（考虑 explicit 和非 explicit 构造函数，但只能调用非 explicit 构造函数）
    \item 以等号后的\textbf{花括号初始化器列表}初始化具名变量
    \item 函数调用表达式中，以\textbf{花括号初始化器列表}为实参，以列表初始化对函数形参初始化
    \item 在以\textbf{花括号初始化器列表}为返回表达式的 return 语句中，以列表初始化对返回的对象初始化
    \item 在具有用户定义的 operator[] 的下标表达式中，以列表初始化对重载运算符的形参初始化
    \item 在赋值表达式中，以列表初始化对重载的运算符的形参初始化
    \item 函数式转型表达式或其他构造函数调用，其中\textbf{花括号初始化器列表}用作构造函数实参。以复制初始化对构造函数的形参初始化（注意：此例中的类型 U 不是被列表初始化的类型；但 U 的构造函数的形参是）
    \item 在使用等号的非静态数据成员初始化器中
\end{enumerate}
T 类型的对象的列表初始化的效果是：
\begin{itemize}
    \item 如果 T 是聚合类且\textbf{花括号初始化器列表}拥有单个（可有 cv 限定的）相同类型或派生类型的元素，那么从该元素初始化对象（对于复制列表初始化为复制初始化，对于直接列表初始化为直接初始化）。
    \item 否则，如果 T 是字符数组且初始化器列表拥有单个类型适当的字符串字面量元素，那么照常从字符串字面量初始化数组。
    \item 否则，如果 T 是聚合类型，那么进行聚合初始化。
    \item 否则，如果\textbf{花括号初始化器列表}列表为空，且 T 是拥有默认构造函数的类类型，那么进行值初始化。
    \item 否则，如果 T 是 std::initializer\_list 的特化，那么依据它所在的语境以\textbf{花括号初始化器列表}所初始化的同类型的纯右值 (C++17 前)直接初始化或复制初始化该 T 对象。
    \item 否则，以两个阶段考虑 T 的构造函数：
        \begin{itemize}
            \item 检验所有接受 std::initializer\_list 作为它的唯一实参，或如果剩余实参都具有默认值则为它的首个实参的构造函数，并通过重载决议与单个 std::initializer\_list 类型的实参进行匹配。
            \item 如果上一个阶段未产生匹配，那么 T 的所有构造函数都参与针对由\textbf{花括号初始化器列表}的各元素所组成的实参集的重载决议，它会受到只允许非窄化转换的限制。如果这个阶段为复制列表初始化产生的最佳匹配是 explicit 构造函数，那么编译失败（注意：简单复制初始化中完全不考虑 explicit 构造函数）。
否则，如果：
                \begin{itemize}
                    \item T 是拥有固定底层类型 U （如果枚举是有作用域枚举且未显式指定底层类型时为 int）的枚举类型，且
                    \item 初始化为直接列表初始化，且
                    \item\textbf{花括号初始化器列表}只有一个能隐式转换成 U 的初始化器 v，且
                    \item 从 v 到 U 的转换不是窄化，
                \end{itemize}
那么以将 v 转换到 U 的结果初始化该枚举。(C++17 起)
             \item 否则（如果 T 不是类类型），如果\textbf{花括号初始化器列表}仅拥有一个元素，且 T 要么不是引用类型，要么是引用类型而它所引用的类型与该元素的类型相同或者是它的基类，那么直接初始化（直接列表初始化时）或复制初始化（复制列表初始化时）这个 T，但不允许窄化转换。
             \item 否则，如果 T 是引用类型，那么：
             \begin{itemize}
                  \item 复制列表初始化一个具有由 T 引用的类型的纯右值临时量，并且将引用绑定到该临时量（如果引用是非 const 左值引用则失败）。(C++17 前)
                  \item 生成一个纯右值。该纯右值以复制列表初始化来初始化它的结果对象。然后用该纯右值直接初始化引用（如果引用是非 const 左值引用则失败）。该临时量的类型是由 T 引用的类型，除非 T 是“到 U 的未知边界的数组的引用”，此时该临时量的类型是声明 U x[] H 中 x 的类型，其中 H 是对应的初始化列表 (C++20 起)。
             \end{itemize}
             \item 否则，如果\textbf{花括号初始化器列表}没有任何元素，那么值初始化 T。
        \end{itemize}
\end{itemize}

\begin{longlisting}
\cppfile{sample/initializer_list_demo.cc}
    \caption{initializer\_list例子}
    \label{code:initializer_list_demo}
\end{longlisting}
\subsection{窄化转换}
列表初始化通过禁止下列转换对隐式转换加以限制：
\begin{itemize}
    \item 从浮点类型到整数类型的转换。
    \item 从 long double 到 double 或 float 的转换，以及从 double 到 float 的转换，除非来源是常量表达式且不发生溢出。
    \item 从整数类型到浮点类型的转换，除非来源是能将它的值完全存储于目标类型的常量表达式。
    \item 从整数或无作用域枚举类型到不能表示原类型所有值的整数类型的转换，除非
        \begin{itemize}
            \item 来源是宽度（记为 w）小于自身类型（或者对于枚举类型来说是底层类型）的位域并且目标类型可以表示宽度是 w 且符号与原类型一致的虚设扩展整数类型的所有值，或者
            \item 来源是能将它的值完全存储于目标类型的常量表达式。
        \end{itemize}
    \item 从指针或成员指针类型到 bool 的转换
\end{itemize}
每个初始化器子句均按顺序早于\textbf{花括号初始化器列表}中后随于它的任何初始化器子句。这与函数调用表达式相反，它是无顺序的 (C++17 前)不确定顺序的 (C++17 起)。

\textbf{花括号初始化器列表} 不是表达式，因此它没有类型，即 decltype({1, 2}) 非良构。没有类型意味着模板类型推导无法推导出与\textbf{花括号初始化器列表}相匹配的类型，因此给定声明 template<class T> void f(T); 则表达式 f({1, 2, 3}) 非良构。然而，模板形参可以另行推导，如 std::vector<int> v(std::istream\_iterator<int>(std::cin), {}) 的情况，其中迭代器类型从首个实参推导，但也被用于第二形参位置。对于使用关键词 auto 的类型推导中有一个例外，在复制列表初始化中将任何\textbf{花括号初始化器列表}均推导为 std::initializer\_list。

同样因为\textbf{花括号初始化器列表}没有类型，所以在将它用作重载函数调用的实参时，适用重载决议的特殊规则。

\nameref{Aggregate}直接从同类型的单元素\textbf{花括号初始化器列表}进行复制/移动初始化，但非聚合体首先考虑 initializer\_list 构造函数：
\begin{cpp}
struct X {}; // 聚合体
 
struct Q     // 非聚合体
{
    Q() = default;
    Q(Q const&) = default;
    Q(std::initializer_list<Q>) {}
};
 
int main()
{
    X x;
    X x2 = X{x}; // 复制构造函数（非聚合初始化）
 
    Q q;
    Q q2 = Q{q}; // 初始化器列表构造函数（非复制构造函数）
}
\end{cpp}
有些编译器（例如 gcc 10）仅在 C++20 模式认为从指针或成员指针到 bool 的转换为窄化。
\begin{cpp}
#include <iostream>
#include <vector>
#include <map>
#include <string>
 
struct Foo
{
    std::vector<int> mem = {1, 2, 3}; // 非静态成员的列表初始化
    std::vector<int> mem2;
    Foo() : mem2{-1, -2, -3} {} // 构造函数中的成员列表初始化
};
 
std::pair<std::string, std::string> f(std::pair<std::string, std::string> p)
{
    return {p.second, p.first}; // return 语句中的列表初始化
}
 
int main()
{
    int n0{};  // 值初始化（为零）
    int n1{1}; // 直接列表初始化
 
    std::string s1{'a', 'b', 'c', 'd'}; // initializer_list 构造函数调用
    std::string s2{s1, 2, 2};           // 常规构造函数调用
    std::string s3{0x61, 'a'}; // initializer_list 构造函数偏好 (int, char)
 
    int n2 = {1}; // 复制列表初始化
    double d = double{1.2}; // 纯右值的列表初始化，然后复制初始化
    auto s4 = std::string{"HelloWorld"}; // 同上， C++17 起不创建临时对象
 
    std::map<int, std::string> m = // 嵌套列表初始化
    {
        {1, "a"},
        {2, {'a', 'b', 'c'}},
        {3, s1}
    };
 
    std::cout << f({"hello", "world"}).first // 函数调用中的列表初始化
              << '\n';
 
    const int (&ar)[2] = {1, 2}; // 绑定左值引用到临时数组
    int&& r1 = {1}; // 绑定右值引用到临时 int
//  int& r2 = {2}; // 错误：不能绑定右值到非 const 左值引用
 
//  int bad{1.0}; // 错误：窄化转换
    unsigned char uc1{10}; // 可以
//  unsigned char uc2{-1}; // 错误：窄化转换
 
    Foo f;
 
    std::cout << n0 << ' ' << n1 << ' ' << n2 << '\n'
              << s1 << ' ' << s2 << ' ' << s3 << '\n';
    for (auto p : m)
        std::cout << p.first << ' ' << p.second << '\n';
    for (auto n : f.mem)
        std::cout << n << ' ';
    for (auto n : f.mem2)
        std::cout << n << ' ';
}
\end{cpp}
输出：
\begin{bash}
world
0 1 1
abcd cd aa
1 a
2 abc
3 abcd
1 2 3 -1 -2 -3
\end{bash}
\subsection{初始化聚合体}\label{Aggregate}
从初始化器列表初始化聚合体。是列表初始化的一种形式。 (C++11 起)


\begin{cpp}
T object = { arg1, arg2, ... };    (1)    
T object { arg1, arg2, ... };    (2)    (since C++11)
T object = { .des1 = arg1 , .des2 { arg2 } ... };    (3)    (since C++20)
T object {.des1 = arg1 , .des2 { arg2 } ... };    (4)    (since C++20)

\end{cpp}
\begin{itemize}
    \item 1,2) 用通常的初始化器列表初始化聚合体。
    \item 3,4) 用指派初始化器初始化聚合体。
\end{itemize}
聚合体 是下列类型之一：
\begin{itemize}
    \item 数组类型
    \item 符合以下条件的类类型（通常是 struct 或 union）
    \begin{itemize}
        \item 没有用户声明的构造函数(C++11 前)
        \item 没有用户提供、继承或 explicit 的构造函数(C++11 起)(C++20 前)
        \item 没有用户声明或继承的构造函数(C++20 起)
    \end{itemize}


\item 没有私有或受保护的直接 (C++17 起)非静态数据成员
\begin{itemize}
    \item 没有基类(C++17 前)
    \item 没有虚基类
    \item 没有私有或受保护的直接基类(C++17 起)
\end{itemize}
\item 没有虚成员函数
\item 没有默认成员初始化器(C++11 起)(C++14 前)
\end{itemize}

聚合体的 元素 有：
\begin{itemize}
    \item 对于数组，按下标顺序包含所有数组元素，或者，
    \item 对于类，按声明顺序包含所有不是匿名位域的非静态数据成员。(C++17 前)
    \item 对于类，按声明顺序包含所有直接基类，然后按声明顺序包含所有不是匿名位域或匿名联合体成员的非静态数据成员。(C++17 起)
\end{itemize}
聚合初始化的效果是：
\begin{enumerate}
    \item 拒绝下列非良构场合：
    \begin{itemize}
        \item 初始化器列表中的初始化子句多于聚合体中的元素，或
        \item 以空初始化器列表（{}）初始化边界未知的数组。
            \begin{cpp}
char cv[4] = {'a', 's', 'd', 'f', 0}; // 错误
int x[] = {};                         // 错误
            \end{cpp}
    \end{itemize}
\item 按以下规则决定 显式初始化元素 ：
\begin{itemize}
    \item 如果初始化器列表是指派初始化器列表（聚合体此时只能具有类类型），每个指派符中的标识符必须指名该类的一个非静态数据成员，并且聚合体中显式初始化元素是这些成员或包含这些成员的元素。(C++20 起)
    \item 否则，如果初始化器列表不为空，那么显式初始化元素是聚合体中的前 n 个元素，其中 n 是初始化器列表中的元素数量。
    \item 否则初始化器列表必须为空，并且不存在显式初始化元素。
如果聚合体是联合体并且显式初始化元素不止一个，那么程序非良构：
        \begin{cpp}
union u { int a; const char* b; };
 
u a = {1};                   // OK：显式初始化成员 a
u b = {0, "asdf"};           // 错误：显式初始化两个成员
u c = {"asdf"};              // 错误：不能以 "asdf" 初始化 int
 
// C++20 指派初始化器列表
u d = {.b = "asdf"};         // OK：可以显示初始化非开头元素
u e = {.a = 1, .b = "asdf"}; // 错误：显式初始化两个成员
        \end{cpp}
\end{itemize}
\item 按元素顺序 (C++11 起)初始化聚合体中的每个元素。也就是说，给定某个元素，它关联的所有的值计算和副作用都会按顺序早于按顺序在它之后的所有元素关联的所有的值计算和副作用。 (C++11 起)
\end{enumerate}
\subsubsection{元素初始化}
对于每个显式初始化元素：
\begin{itemize}
    \item 如果该元素是匿名联合体成员，并且初始化器列表是指派初始化器列表，那么该元素会被指派初始化器列表 {D} 初始化，其中 D 是指名该匿名联合体成员的成员的指派初始化器子句。只能有一个这样的指派初始化器子句。
\begin{cpp}
struct C
{
    union
    {
        int a;
        const char* p;
    };
 
    int x;
} c = {.a = 1, .x = 3}; // 以 1 初始化 c.a 并且以 3 初始化 c.x (C++20 起)
\end{cpp}
\item 否则，该元素会以初始化器列表中对应的初始化器子句复制初始化：(C++20 前)
\item 否则，该元素会以初始化器列表中对应的初始化器子句初始化。如果初始化器列表是指派初始化器列表并且初始化器以 = 开始，那么该初始化是直接初始化，否则该初始化是复制初始化：
(C++20 起)
\begin{itemize}
    \item 如果该初始化器子句是表达式，那么该复制初始化允许隐式转换，但禁止窄化转换 (C++11 起)。
    \item 如果该初始化器子句是嵌套的 花括号初始化器列表 （不是表达式），那么以该子句列表初始化该对应元素，它会 (C++11 起)在该对应元素是子聚合体时递归应用该规则。
        \begin{cpp}
struct A
{
    int x;
 
    struct B
    {
        int i;
        int j;
    } b;
} a = {1, {2, 3}}; // 以 1 初始化 a.x，以 2 初始化 a.b.i，以 3 初始化 a.b.j
 
struct base1 { int b1, b2 = 42; };
 
struct base2
{
    base2()
    {
        b3 = 42;
    }
 
    int b3;
};
 
struct derived : base1, base2
{
    int d;
};
 
derived d1{{1, 2}, {}, 4}; // 以  1 初始化 d1.b1，以 2 初始化 d1.b2，
                           //             以 42 初始化 d1.b3，以 4 初始化 d1.d
derived d2{{}, {}, 4};     // 以  0 初始化 d2.b1，以 42 初始化 d2.b2，
                           //             以 42 初始化 d2.b3，以  4 初始化 d2.d
        \end{cpp}
\end{itemize}
对于不是联合体的聚合体，每个没有显式初始化的元素会按以下规则初始化：

\item 如果该元素有默认成员初始化器，那么以该初始化器初始化该元素。(C++11 起)
\item 否则，如果该元素不是引用，那么以空初始化器列表复制初始化该元素。
\item 否则程序非良构。
\begin{cpp}
struct S
{
    int a;
    const char* b;
    int c;
    int d = b[a];
};
 
// 以 1 初始化 ss.a，
// 以 "asdf" 初始化 ss.b，
// 以表达式 int{} 的值（0）初始化 ss.c，
// 以 ss.b[ss.a] 的值（'s'）初始化 ss.d
S ss = {1, "asdf"};
\end{cpp}
\end{itemize}
\subsubsection{花括号消除}
可消除（省略）环绕嵌套的初始化器列表的花括号，这种情况下，使用所需数量的初始化器子句初始化对应的子聚合体的各个成员或元素，而后继的各个初始化器子句被用来初始化对象中的后续成员。然而，如果对象拥有不带任何成员的子聚合体（空结构体，或只保有静态成员的结构体），那么不允许消除花括号而必须使用一个空的嵌套列表 {}。
\subsubsection{指派初始化器}
语法形式 (3,4) 被称为指派初始化器：每个 指派符 必须指名 T 的一个直接非静态数据成员，而表达式中所用的所有 指派符 必须按照与 T 的数据成员相同的顺序出现。
\begin{cpp}
struct A { int x; int y; int z; };
 
A a{.y = 2, .x = 1}; // 错误：指派符的顺序不匹配声明顺序
A b{.x = 1, .z = 2}; // OK：b.y 被初始化为 0
\end{cpp}
指派初始化器所指名的每个直接非静态数据成员，从它的指派符后随的对应花括号或等号初始化器初始化。禁止窄化转换。

指派初始化器可以用来将联合体初始化为它的首个成员之外的状态。只可以为一个联合体提供一个初始化器。
\begin{cpp}
union u { int a; const char* b; };
 
u f = {.b = "asdf"};         // OK：联合体的活跃成员是 b
u g = {.a = 1, .b = "asdf"}; // 错误：只可提供一个初始化器
\end{cpp}
对于非联合体的聚合体中未提供指派初始化器的元素，按上述针对初始化器子句的数量少于成员数量时的规则进行初始化（如果提供默认成员初始化器就使用它，否则用空列表进行初始化）：
\begin{cpp}
struct A
{
    string str;
    int n = 42;
    int m = -1;
};
 
A{.m = 21}  // 以 {} 初始化 str，这样会调用默认构造函数
            // 然后以 = 42 初始化 n
            // 然后以 = 21 初始化 m
\end{cpp}
如果以指派初始化器子句初始化的聚合体拥有一个匿名联合体成员，那么对应的指派初始化器必须指名该匿名联合体的其中一个成员。

注意：乱序的指派初始化、嵌套的指派初始化、指派初始化器与常规初始化器的混合，以及数组的指派初始化在 C 编程语言中受支持，但在 C++ 不允许。
\begin{cpp}
struct A { int x, y; };
struct B { struct A a; };
 
struct A a = {.y = 1, .x = 2}; // C 中合法，C++ 中非法（乱序）
int arr[3] = {[1] = 5};        // C 中合法，C++ 中非法（数组）
struct B b = {.a.x = 0};       // C 中合法，C++ 中非法（嵌套）
struct A a = {.x = 1, 2};      // C 中合法，C++ 中非法（混合）
(C++20 起)
\end{cpp}
\subsection{字符数组}
通常字符类型（char、signed char、unsigned char）、char8\_t (C++20 起)、char16\_t、char32\_t (C++11 起) 或 wchar\_t 的数组能分别从通常字符串字面量、UTF-8 字符串字面量 (C++20 起)、UTF-16 字符串字面量、UTF-32 字符串字面量 (C++11 起)或宽字符串字面量初始化，可以以花括号环绕。另外， char 或 unsigned char 的数组可以由 UTF-8 字符串字面量初始化，可用花括号环绕字符串字面量。 (C++20 起)字符串字面量的相继字符（包含隐含的空终止字符）初始化各数组元素。如果数组大小被指定且大于字符串字面量中的字符数，那么剩余字符会被零初始化。
\begin{cpp}
char a[] = "abc";
// 等价于 char a[4] = {'a', 'b', 'c', '\0'};
 
//  unsigned char b[3] = "abc"; // 错误：初始化器字符串太长
unsigned char b[5]{"abc"};
// 等价于 unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};
 
wchar_t c[] = {L"кошка"}; // 花括号可以省略
// 等价于 wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};
\end{cpp}
注解
聚合类或数组可以包含非聚合的公开基类、 (C++17 起)成员或元素，它们以上述方式初始化（例如从对应的初始化器子句复制初始化）

C++11 前，聚合初始化中曾允许窄化转换，但此后不再被允许。

C++11 前，由于语法限制，聚合初始化只能用于变量定义，而不能用于构造函数初始化器列表、 new 表达式或临时对象创建。

C 中，长度比字符串字面量的大小少一的字符数组可以从字符串字面量初始化；产生的数组是非空终止的。这在 C++ 中不允许。

示例
运行此代码
\begin{cpp}
#include <string>
#include <array>
 
struct S
{
    int x;
 
    struct Foo
    {
        int i;
        int j;
        int a[3];
    } b;
};
 
int main()
{
    S s1 = {1, {2, 3, {4, 5, 6}}};
    S s2 = {1, 2, 3, 4, 5, 6};  // 相同，但有花括号消除
    S s3{1, {2, 3, {4, 5, 6}}}; // 相同，使用直接列表初始化语法
    S s4{1, 2, 3, 4, 5, 6}; // CWG 1270 前错误：花括号消除只能与等号一起使用
 
    int ar[] = {1, 2, 3}; // ar 是 int[3]
//  char cr[3] = {'a', 'b', 'c', 'd'}; // 初始化器子句过多
    char cr[3] = {'a'}; // 数组初始化为 {'a', '\0', '\0'}
 
    int ar2d1[2][2] = {{1, 2}, {3, 4}}; // 完全花括号的 2D 数组： {1, 2}
                                        //                      {3, 4}
    int ar2d2[2][2] = {1, 2, 3, 4}; // 花括号消除： {1, 2}
                                    //             {3, 4}
    int ar2d3[2][2] = {{1}, {2}};   // 仅第一列： {1, 0}
                                    //           {2, 0}
 
    std::array<int, 3> std_ar2{{1, 2, 3}};  // std::array 是聚合体
    std::array<int, 3> std_ar1 = {1, 2, 3}; // 可以消除花括号
 
//  int ai[] = { 1, 2.0 }; // 从 double 到 int 的窄化转换：
                           // C++11 中错误，C++03 中 OK
 
    std::string ars[] = {std::string("one"), // 复制初始化
                         "two",              // 转换，然后复制初始化
                         {'t', 'h', 'r', 'e', 'e'}}; // 列表初始化
 
    union U
    {
        int a;
        const char* b;
    };
    U u1 = {1};         // OK，联合体的首个成员
//  U u2 = {0, "asdf"}; // 错误：用过多的初始化器初始化联合体
//  U u3 = {"asdf"};    // 错误：到 int 的转换无效
 
    [](auto...) { std::puts("废弃无用的变量…… 完成。"); }
    (
        s1, s2, s3, s4, ar, cr, ar2d1, ar2d2, ar2d3, std_ar2, std_ar1, u1
    );
}
 
// 聚合体
struct base1 { int b1, b2 = 42; };
 
// 非聚合体
struct base2
{
    base2() : b3(42) {}
 
    int b3;
};
 
// C++17 里是聚合体
struct derived : base1, base2 { int d; };
 
derived d1{{1, 2}, {}, 4}; // d1.b1 = 1，d1.b2 = 2 ，d1.b3 = 42，d1.d = 4
derived d2{{}, {}, 4};     // d2.b1 = 0，d2.b2 = 42，d2.b3 = 42，d2.d = 4
\end{cpp}
输出：

废弃无用的变量…… 完成。
